

// 49.3 定义宏macro_rules!
//macro_rules_2
/*

Rust provides a powerful macro system that allows metaprogramming. As you've seen in previous chapters, macros look like functions, except that their name ends with a bang !, but instead of generating a function call, macros are expanded into source code that gets compiled with the rest of the program.

Macros are created using the macro_rules! macro.
*/

// simple.rs
// macros are behind a feature gate
// #![feature(macro_rules)]

// This is the simplest macro, `say_hello` is the name of the macro
macro_rules! say_hello {
    // `()` indicates that the macro takes no argument
    () => {
        // the macro will expand into the contents of this block
        println!("Hello!");
    }
}

pub fn test_fn() {
    // this call will expand into `println!("Hello");`
    say_hello!()
}


/*

{simple.out}

The arguments of a macro are prefixed by a dollar sign $ and type annotated with a designator.
宏的参数以$符号$开头，并用指示器注释类型。
*/

// designators.rs

macro_rules! create_function {
    // this macro takes an argument of "type" `ident`
    // 这个宏的参数是“type”“ident”  ident 是关键字
    // the `ident` designator is used for variable/function names
    // “ident”指示器用于变量/函数名
    ($func_name:ident) => {
        // this macro creates a function with name `$func_name`
        // 这个宏创建了一个名为“$func_name”的函数
        fn $func_name() {
            // the stringify! macro converts an `ident` into a string
            // 函数的把!宏将' ident '转换为字符串
            println!("You called {}()",
                     stringify!($func_name))
        }
    }
}

create_function!(foo);// 打印 You called foo()
create_function!(bar);// 打印 You called bar()

macro_rules! print_result {
    // the `expr` designator is used for expressions
    // “expr”指示器用于表达式
    ($expression:expr) => {
        // stringify! will convert the expression *as it is* into a string
        // stringify !将表达式*转换为字符串吗
        println!("{} = {}", // 打印格式
            stringify!($expression), // 打印表达式内容
            $expression) // 打印表达式计算结果
    }
}

pub fn test_fn1() {
    foo();// 打印 You called foo()
    bar();// 打印 You called bar()

    print_result!(1u32 + 1); // 1u32 + 1 = 2

    // remember that blocks are expressions
    // 记住block是表达式
    print_result!({
        let x = 1u32; 
        x * x + 2 * x - 1 
    });
    // { let x = 1u32; x * x + 2 * x - 1 } = 2
    // 等号前是完整表达式(这里是多个表达式) 等号 后 是 计算结果
}

/*
{designators.out}
Macros can be overloaded to accept different combinations of arguments.
宏可以重载以接受不同的参数组合。
*/

// overload.rs

// macro_rules! is similar to a match block
// macro_rules! 类似于匹配块
macro_rules! test {
    // the arguments don't need to be separated by a comma
    // 参数不需要用逗号分隔
    // any template can be used
    // 任何模板都可以使用
    ($left:expr; and $right:expr) => {
        println!("{:?} and {:?} is {:?}",  // 这里 {:?} 和 {} 打印，{:?} 会带上外层双引号
                 stringify!($left),
                 stringify!($right),
                 $left && $right)
    };
    // ^ each arm must be ended with a semicolon
    // ^每个部门(部分)必须以分号结束  最后一个部分可以省略分号
    ($left:expr; or $right:expr) => {
        println!("{} or {} is {}", // 这里 {:?} 和 {} 打印，{} 没有外层双引号
                 stringify!($left),
                 stringify!($right),
                 $left || $right)
    };
}

pub fn test_fn2() {
    // "1i32 + 1 == 2i32" and "2i32 * 2 == 4i32" is true
    test!(1i32 + 1 == 2i32; and 2i32 * 2 == 4i32);
    // "true" or "false" is true
    test!(true; or false);
}

/*
{overload.out}

Macros can use + in the argument list, to indicate that an argument may repeat at least once, or *, to indicate that the argument may repeat zero or more times.
宏可以在参数列表中使用+，表示一个参数可能至少重复一次，或*表示参数可能重复零次或多次。
*/

// repeat.rs

// min! will calculate the minimum of any number of arguments
// min! 将计算任何数量的参数的最小值
macro_rules! min {
    // base case  一个表达式，直接返回
    ($x:expr) => {
        $x
    };
    // `$x` followed by at least one `$y,`
    // “$x”后面跟着至少一个“$y”
    ($x:expr, $($y:expr),+) => {
        // call min! on the tail `$y`
        // 呼叫 min!宏  后面是$y  递归宏
        std::cmp::min($x, min!($($y),+)) //std::cmp::min 求两个中最小的
    }
}

pub fn test_fn3() {
    println!("{}", min!(1u32));// 1  一个表达式直接返回
    println!("{}", min!(1u32 + 2 , 2u32));//1u32+2=3 ，2  min 2 小
    println!("{}", min!(5u32, 2u32 * 3, 4u32));// 5,6,4   4最小 输出
}
// 以上可见 宏 很强大！！

/*
{repeat.out}

Macros allow writing DRY code, by factoring out the common parts of functions and/or test suites. Here is an example that implements and tests the +=, *= and -= operators on Vec<T>.
宏允许通过分解函数和/或测试套件的公共部分来编写干代码。下面的示例实现并测试Vec<T>上的+=、*=和-=操作符。
*/

// dry.rs
use std::ops::{Add, Mul, Sub};

macro_rules! assert_equal_len {
    // `tt` （token tree，令牌树）指示符用于运算符和令牌。
    // （原文：The `tt` (token tree) designator is used for
    // operators and tokens.）
    ($a:ident, $b: ident, $func:ident, $op:tt) => (
        assert!($a.len() == $b.len(),
                "{:?}: dimension mismatch: {:?} {:?} {:?}",
                stringify!($func),
                ($a.len(),),
                stringify!($op),
                ($b.len(),));
    )
}

macro_rules! op {
    ($func:ident, $bound:ident, $op:tt, $method:ident) => (
        fn $func<T: $bound<T, Output=T> + Copy>(xs: &mut Vec<T>, ys: &Vec<T>) {
            assert_equal_len!(xs, ys, $func, $op);

            for (x, y) in xs.iter_mut().zip(ys.iter()) {
                *x = $bound::$method(*x, *y);
                // *x = x.$method(*y);
            }
        }
    )
}

// 实现 `add_assign`、`mul_assign` 和 `sub_assign` 等函数。
op!(add_assign, Add, +=, add);
op!(mul_assign, Mul, *=, mul);
op!(sub_assign, Sub, -=, sub);

mod mod_test_4 {
    use std::iter;
    macro_rules! test1 {
        ($func: ident, $x:expr, $y:expr, $z:expr) => {
            fn $func() {
                for size in 0usize..10 {
                    let mut x: Vec<_> = iter::repeat($x).take(size).collect();
                    let y: Vec<_> = iter::repeat($y).take(size).collect();
                    let z: Vec<_> = iter::repeat($z).take(size).collect();

                    super::$func(&mut x, &y);

                    assert_eq!(x, z);
                }
            }
        }
    }

    // 测试 `add_assign`、`mul_assign` 和 `sub_assign`
    pub fn run_test(){
        println!("==test_fn4::run_test()==");
        test1!(add_assign, 1u32, 2u32, 3u32);
        test1!(mul_assign, 2u32, 3u32, 6u32);
        test1!(sub_assign, 3u32, 2u32, 1u32);
    }
}

pub fn test_fn4(){
    mod_test_4::run_test();
}

/*
{dry.out}

$ rustc --test dry.rs && ./dry
running 3 tests
test test::mul_assign ... ok
test test::add_assign ... ok
test test::sub_assign ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured

*/

